package com.fy.fyspace.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fy.fyspace.common.manager.CosManager;
import com.fy.fyspace.common.result.R;
import com.fy.fyspace.common.utils.ImageUtil;
import com.fy.fyspace.constant.KeysConstant;
import com.fy.fyspace.constant.ResultMessageConstant;
import com.fy.fyspace.ex.ConditionException;
import com.fy.fyspace.ex.handler.ExHandler;
import com.fy.fyspace.mapper.*;
import com.fy.fyspace.model.dto.picture.*;
import com.fy.fyspace.model.entity.*;
import com.fy.fyspace.model.enums.picture.PictureDeleteStatusEnum;
import com.fy.fyspace.model.enums.picture.PictureReviewStatusEnum;
import com.fy.fyspace.model.enums.user.UserRoleEnum;
import com.fy.fyspace.model.mq.ImageUploadMessage;
import com.fy.fyspace.model.vo.picture.ImageMessage;
import com.fy.fyspace.model.vo.picture.PictureVO;
import com.fy.fyspace.model.vo.user.UserSessionVO;
import com.fy.fyspace.service.PictureService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.fy.fyspace.common.result.R.no;
import static com.fy.fyspace.common.result.R.ok;
import static com.fy.fyspace.constant.KeysConstant.*;
import static com.fy.fyspace.constant.SysConstant.*;


@Service
@Slf4j
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture> implements PictureService {

    @Resource
    private ThreadPoolTaskExecutor imageProcessThreadPool;

    @Resource
    private SpaceMapper spaceMapper;

    @Resource
    private HttpSession httpSession;

    @Resource
    private PictureMapper pictureMapper;

    @Resource
    private LikePictureMapper likePictureMapper;

    @Resource
    private FavoritePictureMapper favoritePictureMapper;

    @Resource
    private AipictureMapper aipictureMapper;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private CosManager cosManager;


    /**获取图片列表
     * 
     * @param pictureListQueryDTO
     * @return R
     */
    @Override
    public R getPictureList(PictureListQueryDTO pictureListQueryDTO) {
        String category = pictureListQueryDTO.getCategory();
        String tags = pictureListQueryDTO.getTags();
        String description = pictureListQueryDTO.getDescription();
        Integer page = pictureListQueryDTO.getPage();
        Integer pageSize = pictureListQueryDTO.getPageSize();
        String type = pictureListQueryDTO.getType();
        String pageType = pictureListQueryDTO.getPageType();
        String searchText = pictureListQueryDTO.getSearchText();

            log.info("开始获取图片列表: type={}, page={}, pageSize={}, category={}, tags={}, searchText={}",
                    type, page, pageSize, category, tags, description);

            // 1. 参数预处理
            page = page == null ? 1 : page;
            pageSize = pageSize == null ? DEFAULT_PAGE_SIZE : pageSize;

            ExHandler.throwIf(page > 1000, "别你妈恶意搞我！");
            ExHandler.throwIf(pageSize > 100, "一次最多只能获取100张图片");
            // 2. 构建查询条件
            LambdaQueryWrapper<Picture> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Picture::getIsDelete, PictureDeleteStatusEnum.NOT_DELETE.getValue())
                    .eq(Picture::getReviewStatus, PictureReviewStatusEnum.PASS.getValue());

            UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
            if ("self".equals(type)) {
                ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.NOT_LOGIN);
            }
            // 添加基本查询条件
            boolean hasFilters = false;
            if (StrUtil.isNotBlank(category)) {
                queryWrapper.eq(Picture::getCategory, category);
                hasFilters = true;
            }
            if (StrUtil.isNotBlank(searchText)) {
                queryWrapper.eq(Picture::getName, searchText);
                hasFilters = true;
            }
            if (StrUtil.isNotBlank(tags)) {
                log.debug("处理标签查询条件: tags={}", tags);
                String[] tagArray = tags.split(",");
                for (String tag : tagArray) {
                    tag = tag.trim();
                    if (StrUtil.isNotBlank(tag)) {
                        queryWrapper.like(Picture::getTags, tag);
                    }
                }
                hasFilters = true;
            }
            if (StrUtil.isNotBlank(description)) {
                log.debug("处理搜索文本查询条件: searchText={}", description);
                queryWrapper.and(wrapper -> wrapper
                        .like(Picture::getName, description)
                        .or()
                        .like(Picture::getIntroduction, description));
                hasFilters = true;
            }

            // 添加随机排序
            queryWrapper.last("ORDER BY RAND()");

            // 3. 处理个人空间查询
            if ("self".equals(type)) {
                if (userSessionVO == null) {
                    log.warn("获取个人空间图片列表失败：用户未登录");
                    throw new ConditionException(ResultMessageConstant.NOT_LOGIN);
                }
                log.info("查询个人空间图片列表: userId={}", userSessionVO.getId());
                queryWrapper.eq(Picture::getUserId, userSessionVO.getId());

                // 个人空间直接查询数据库
                PageHelper.startPage(page, pageSize);
                List<Picture> pictureList = pictureMapper.selectList(queryWrapper);
                PageInfo<Picture> pageInfo = new PageInfo<>(pictureList);
                log.info("从数据库获取个人空间图片列表成功: userId={}, total={}",
                        userSessionVO.getId(), pageInfo.getTotal());

                List<PictureVO> pictureVOList = pictureList.stream()
                        .map(PictureVO::objToVo)
                        .collect(Collectors.toList());

                Map<String, Object> result = new HashMap<>();
                result.put("records", pictureVOList);
                result.put("total", pageInfo.getTotal());
                result.put("size", pageInfo.getPageSize());
                result.put("current", pageInfo.getPageNum());
                result.put("pages", pageInfo.getPages());

                return ok(result);
            }

            // 4. 处理公共空间查询
            if ("all".equals(type)) {
                // 严格判断：只有page==1，type==all，且所有查询条件都为空，才走缓存
                boolean useCache = (page == 1)
                        && ("all".equalsIgnoreCase(type))
                        && StrUtil.isBlank(category)
                        && StrUtil.isBlank(tags)
                        && StrUtil.isBlank(description)
                        && StrUtil.isBlank(searchText);

                if (!useCache) {
                    // 只要有任何查询条件，直接查数据库
                    log.info("检测到筛选条件或非首页，直接查询数据库: useCache=false");
                    PageHelper.startPage(page, pageSize);
                    List<Picture> pictureList = pictureMapper.selectList(queryWrapper);
                    PageInfo<Picture> pageInfo = new PageInfo<>(pictureList);
                    log.info("从数据库获取筛选后的图片列表成功: total={}", pageInfo.getTotal());

                    List<PictureVO> pictureVOList = pictureList.stream()
                            .map(PictureVO::objToVo)
                            .collect(Collectors.toList());

                    Map<String, Object> result = new HashMap<>();
                    result.put("records", pictureVOList);
                    result.put("total", pageInfo.getTotal());
                    result.put("size", pageInfo.getPageSize());
                    result.put("current", pageInfo.getPageNum());
                    result.put("pages", pageInfo.getPages());

                    return ok(result);
                }

                // 只有严格满足条件才走缓存
                String cacheKey = generateCacheKey(page, pageSize);
                log.debug("尝试从缓存获取数据: cacheKey={}", cacheKey);

                // 4.1 检查缓存是否处于更新状态
                Boolean isUpdating = (Boolean) redisTemplate.opsForValue().get(CACHE_UPDATING_KEY + cacheKey);
                if (Boolean.TRUE.equals(isUpdating)) {
                    log.debug("缓存正在更新中，尝试使用旧缓存: cacheKey={}", cacheKey);
                    String oldData = (String) redisTemplate.opsForValue().get(CACHE_DATA_KEY + cacheKey);
                    if (StrUtil.isNotBlank(oldData)) {
                        log.info("使用旧缓存数据成功: cacheKey={}", cacheKey);
                        Map<String, Object> result = JSONUtil.toBean(oldData, Map.class);
                        return ok(result);
                    }
                }

                // 4.2 检查缓存是否存在且未过期
                String cachedData = (String) redisTemplate.opsForValue().get(CACHE_DATA_KEY + cacheKey);
                Boolean isLogicDeleted = (Boolean) redisTemplate.opsForValue().get(CACHE_LOGIC_DELETE_KEY + cacheKey);

                if (StrUtil.isNotBlank(cachedData) && !Boolean.TRUE.equals(isLogicDeleted)) {
                    log.info("从缓存获取数据成功: cacheKey={}", cacheKey);
                    Map<String, Object> result = JSONUtil.toBean(cachedData, Map.class);
                    return ok(result);
                }

                // 4.3 尝试获取更新锁
                Boolean acquiredLock = redisTemplate.opsForValue().setIfAbsent(
                        CACHE_UPDATING_KEY + cacheKey, true, 30, TimeUnit.SECONDS);

                if (Boolean.TRUE.equals(acquiredLock)) {
                    log.debug("获取到缓存更新锁，开始更新缓存: cacheKey={}", cacheKey);
                    try {
                        // 获得锁的线程负责更新缓存
                        log.info("开始执行分页查询: page={}, pageSize={}", page, pageSize);
                        PageHelper.startPage(page, pageSize);
                        List<Picture> pictureList = pictureMapper.selectList(queryWrapper);
                        PageInfo<Picture> pageInfo = new PageInfo<>(pictureList);
                        log.info("分页查询结果: 当前页={}, 每页大小={}, 总记录数={}, 总页数={}",
                                pageInfo.getPageNum(), pageInfo.getPageSize(),
                                pageInfo.getTotal(), pageInfo.getPages());

                        List<PictureVO> pictureVOList = pictureList.stream()
                                .map(PictureVO::objToVo)
                                .collect(Collectors.toList());

                        // 更新缓存
                        Map<String, Object> pageResult = new HashMap<>();
                        pageResult.put("records", pictureVOList);
                        pageResult.put("total", pageInfo.getTotal());
                        pageResult.put("size", pageInfo.getPageSize());
                        pageResult.put("current", pageInfo.getPageNum());
                        pageResult.put("pages", pageInfo.getPages());

                        String newData = JSONUtil.toJsonStr(pageResult);
                        redisTemplate.opsForValue().set(CACHE_DATA_KEY + cacheKey, newData, CACHE_EXPIRE_TIME,
                                TimeUnit.MINUTES);
                        redisTemplate.opsForValue().set(CACHE_LOGIC_DELETE_KEY + cacheKey, false, CACHE_EXPIRE_TIME,
                                TimeUnit.MINUTES);
                        log.info("缓存更新成功: cacheKey={}", cacheKey);

                        // 删除旧缓存和更新标记
                        redisTemplate.delete(CACHE_UPDATING_KEY + cacheKey);
                        log.info("最终返回数据:{}", pageResult);
                        return ok(pageResult);
                    } finally {
                        redisTemplate.delete(CACHE_UPDATING_KEY + cacheKey);
                        log.debug("释放缓存更新锁: cacheKey={}", cacheKey);
                    }
                } else {
                    log.debug("未获取到缓存更新锁，尝试使用旧数据: cacheKey={}", cacheKey);
                    // 未获得锁的线程使用旧数据
                    if (StrUtil.isNotBlank(cachedData)) {
                        log.info("使用旧缓存数据: cacheKey={}", cacheKey);
                        Map<String, Object> result = JSONUtil.toBean(cachedData, Map.class);
                        return ok(result);
                    }

                    // 如果没有旧数据，执行查询
                    log.info("无可用缓存，直接查询数据库");
                    PageHelper.startPage(page, pageSize);
                    List<Picture> pictureList = pictureMapper.selectList(queryWrapper);
                    PageInfo<Picture> pageInfo = new PageInfo<>(pictureList);
                    log.info("从数据库获取数据成功: total={}", pageInfo.getTotal());

                    List<PictureVO> pictureVOList = pictureList.stream()
                            .map(PictureVO::objToVo)
                            .collect(Collectors.toList());

                    Map<String, Object> result = new HashMap<>();
                    result.put("records", pictureVOList);
                    result.put("total", pageInfo.getTotal());
                    result.put("size", pageInfo.getPageSize());
                    result.put("current", pageInfo.getPageNum());
                    result.put("pages", pageInfo.getPages());

                    return ok(result);
                }
            }
            log.warn("无效的查询类型: type={}", type);
            throw new ConditionException("Invalid type parameter");
    }

    /**
     * 生成缓存键
     * 只使用分页参数生成缓存键，因为只缓存默认查询
     */
    private String generateCacheKey(Integer page, Integer pageSize) {
        return String.format("default:%d:%d", page, pageSize);
    }

    /**
     * 上传文件
     *
     * @param files       图片文件数组
     * @param namePrefix  文件名前缀
     * @param tags        标签字符串，以逗号分隔
     * @param category    分类
     * @param description 描述
     * @param isPublic    是否公开
     * @return
     */
    @Override
    public R uploadImages(MultipartFile[] files,
                               String namePrefix,
                               String tags,
                               String category,
                               String description,
                               Integer isPublic) {

        log.info("开始处理图片上传请求: fileCount={}, namePrefix={}, category={}, isPublic={}",
                files != null ? files.length : 0, namePrefix, category, isPublic);

        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        if (userSessionVO == null) {
            log.warn("图片上传失败：用户未登录");
            throw new ConditionException(ResultMessageConstant.NOT_LOGIN);
        }

        //标签最多有4个
        if (StrUtil.isNotBlank(tags)) {
            String[] tagArray = tags.split(",");
            if (tagArray.length > 4) {
                log.warn("图片上传失败：标签数量超过限制");
                throw new ConditionException("标签数量超过限制");
            }
        }

        // 验证文件
        ExHandler.throwIf(files == null || files.length == 0,"请选择要上传的图片");

        // 获取用户空间信息
        Space space = spaceMapper.selectById(userSessionVO.getId());
        ExHandler.throwIf(space == null,ResultMessageConstant.SPACE_NOT_FOUND);

        // 非管理员用户进行文件大小和空间容量验证
        if (!UserRoleEnum.ADMIN.getDesc().equals(userSessionVO.getUserRole())) {
            log.debug("执行非管理员用户的空间容量验证: userId={}", userSessionVO.getId());
            long totalSize = 0;
            for (MultipartFile file : files) {
                if (file.getSize() > 30 * 1024 * 1024) {
                    log.warn("文件大小超过限制: fileName={}, size={}KB",
                            file.getOriginalFilename(), file.getSize() / 1024);
                    throw new ConditionException(ResultMessageConstant.FILE_SIZE_ERROR);
                }
                totalSize += file.getSize();
            }

            // 校验空间容量
            if (space.getTotalSize() + totalSize > space.getMaxSize()
                    || space.getTotalCount() + files.length > space.getMaxCount()) {
                log.warn("用户空间容量不足: userId={}, currentSize={}MB, requiredSize={}MB, currentCount={}, requiredCount={}",
                        userSessionVO.getId(), space.getTotalSize() / (1024 * 1024),
                        totalSize / (1024 * 1024), space.getTotalCount(), files.length);
                throw new ConditionException(ResultMessageConstant.INSUFFICIENT_SPACE);
            }
        }

        // 生成批次ID
        String batchId = UUID.randomUUID().toString().replace("-", "");
        log.info("开始批量处理图片上传: batchId={}, fileCount={}", batchId, files.length);

        try {
            // 预先生成所有文件名
            Map<Integer, String> fileNames = new HashMap<>();
            int startIndex = 1;

            if (StrUtil.isNotBlank(namePrefix)) {
                // 如果指定了前缀,查询该前缀下的最大序号
                LambdaQueryWrapper<Picture> wrapper = new LambdaQueryWrapper<>();
                wrapper.select(Picture::getName)
                        .like(Picture::getName, namePrefix + "_")
                        .orderByDesc(Picture::getName);
                List<Picture> pictures = pictureMapper.selectList(wrapper);

                if (!pictures.isEmpty()) {
                    for (Picture picture : pictures) {
                        String name = picture.getName();
                        int underscoreIndex = name.lastIndexOf('_');
                        if (underscoreIndex > -1) {
                            String numberPart = name.substring(underscoreIndex + 1);
                            int dotIndex = numberPart.lastIndexOf('.');
                            if (dotIndex > -1) {
                                numberPart = numberPart.substring(0, dotIndex);
                            }
                            try {
                                int currentIndex = Integer.parseInt(numberPart);
                                startIndex = Math.max(startIndex, currentIndex + 1);
                            } catch (NumberFormatException e) {
                                log.warn("无法解析文件名序号: {}", name);
                            }
                        }
                    }
                }
            }

            // 为每个文件生成唯一文件名
            for (int i = 0; i < files.length; i++) {
                String originalFilename = files[i].getOriginalFilename();
                String extension = "";
                int dotIndex = originalFilename.lastIndexOf('.');
                if (dotIndex > 0) {
                    extension = originalFilename.substring(dotIndex + 1);
                }
                if (extension.isEmpty()) {
                    extension = "jpg";
                }

                String fileName;
                if (StrUtil.isBlank(namePrefix)) {
                    // 使用时间戳+索引生成文件名
                    fileName = System.currentTimeMillis() + "_" + (i + 1) + "." + extension;
                } else {
                    // 使用前缀+递增序号生成文件名
                    fileName = namePrefix + "_" + (startIndex + i) + "." + extension;
                }
                fileNames.put(i, fileName);
                log.debug("生成文件名: index={}, fileName={}", i, fileName);
            }

            // 使用CountDownLatch等待所有文件处理完成
            CountDownLatch latch = new CountDownLatch(files.length);

            // 并发处理每个文件
            for (int i = 0; i < files.length; i++) {
                final int index = i;
                final MultipartFile file = files[i];
                final String fileName = fileNames.get(index);

                CompletableFuture.runAsync(() -> {
                    String nm = fileName.substring(0, fileName.lastIndexOf("."));
                    log.debug("开始处理文件: fileName={}, nm, index={}/{}", fileName, nm, index + 1, files.length);
                    try {
                        processFile(file, fileName, userSessionVO, space.getId(), tags, category, description, batchId, nm);
                    } catch (Exception e) {
                        log.error("文件处理失败: fileName={}, error={}", fileName, e.getMessage());
                        try {
                            ImageMessage imageInfo = ImageUtil.getImageMessage(file);
                            sendErrorLog(file, fileName, userSessionVO.getId(), batchId, e.getMessage(), imageInfo);
                        } catch (Exception ex) {
                            sendErrorLog(file, fileName, userSessionVO.getId(), batchId, e.getMessage(), null);
                        }
                    } finally {
                        latch.countDown();
                        log.debug("文件处理完成: fileName={}, remainingCount={}", fileName, latch.getCount());
                    }
                }, imageProcessThreadPool);
            }

            // 等待所有文件处理完成，最多等待5分钟
            boolean completed = latch.await(5, TimeUnit.MINUTES);
            if (!completed) {
                log.warn("文件处理超时: batchId={}", batchId);
                throw new ConditionException("文件处理超时，请稍后查看处理结果");
            }
            log.info("批量文件处理完成: batchId={}, fileCount={}", batchId, files.length);
            return ok("文件上传请求已提交");

        } catch (Exception e) {
            log.error("文件处理失败: batchId={}, error={}", batchId, e.getMessage());
            throw new ConditionException(e.getMessage());
        }
    }

    /**
     * 处理单个文件上传
     */
    private void processFile(MultipartFile file, String fileName, UserSessionVO userSessionVO,
                             Long spaceId, String tags, String category, String description, String batchId, String nm)
            throws Exception {

        // 1. 校验图片
        ImageUtil.validate(file);

        // 2. 获取图片信息
        ImageMessage imageInfo = ImageUtil.getImageMessage(file);

        try {
            // 3. 创建临时文件
            File tempFile = File.createTempFile("upload_", "_" + fileName);
            try {
                file.transferTo(tempFile);

                // 4. 上传到COS
                boolean isAdmin = UserRoleEnum.ADMIN.getType().equals(userSessionVO.getUserRole());
                String basePath = isAdmin ? "/public/" : "/user/";
                String cosPath = basePath + fileName;
                cosManager.putObject(cosPath, tempFile);

                // 获取图片颜色信息
                String mainColor = ImageUtil.getImageMainColor("https://fy-space-1333414113.cos.ap-beijing.myqcloud.com" + cosPath + "?imageAve");


                log.info("图片颜色信息: color={}", mainColor);

                // 5. 构建WebP访问路径
                String webpPath = cosPath + "?imageMogr2/format/webp/quality/60";

                // 6. 保存图片信息到数据库
                Picture picture = new Picture();
                picture.setUserId(userSessionVO.getId());

                picture.setName(StrUtil.subBefore(StrUtil.subAfter(cosPath, "/", true), ".", true));
                picture.setUrl(cosPath);
                picture.setThumbnailUrl(webpPath);
                picture.setTagsList(StrUtil.isNotBlank(tags) ? JSONUtil.toList(tags, String.class) : null);
                picture.setCategory(category);
                picture.setIntroduction(description);
                if (isAdmin) {
                    picture.setSpaceId(null);
                } else {
                    picture.setSpaceId(spaceId);
                }

                picture.setFileSize(file.getSize());
                picture.setPicWidth(imageInfo.getPicWidth());
                picture.setPicHeight(imageInfo.getPicHeight());
                picture.setPicScale(imageInfo.getPicScale());
                picture.setPicFormat(imageInfo.getPicFormat());
                picture.setPicColor(mainColor);
                picture.setPicSize(imageInfo.getPicSize());
                picture.setIsDelete(PictureDeleteStatusEnum.NOT_DELETE.getValue());
                picture.setReviewStatus(isAdmin ? PictureReviewStatusEnum.PASS.getValue()
                        : PictureReviewStatusEnum.REVIEWING.getValue());
                picture.setCreateTime(new Timestamp(System.currentTimeMillis()));
                picture.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                log.info("保存图片信息到数据库: {}", JSONUtil.toJsonStr(picture));
                pictureMapper.insert(picture);

                // 7. 更新用户空间信息
                Space space = spaceMapper.selectById(spaceId);
                if (space != null) {
                    space.setTotalSize(space.getTotalSize() + file.getSize());
                    space.setTotalCount(space.getTotalCount() + 1);
                    space.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                    spaceMapper.updateById(space);
                }

                log.info("文件处理成功: fileName={}, size={}KB, format={}, width={}px, height={}px",
                        fileName, file.getSize() / 1024, imageInfo.getPicFormat(),
                        imageInfo.getPicWidth(), imageInfo.getPicHeight());

            } finally {
                // 8. 清理临时文件
                tempFile.delete();
            }
        } catch (Exception e) {
            log.error("文件处理失败: {}", fileName, e);
            sendErrorLog(file, fileName, userSessionVO.getId(), batchId, e.getMessage(), imageInfo);
            throw e;
        }
    }

    /**
     * 发送错误日志
     */
    private void sendErrorLog(MultipartFile file, String fileName, Long userId, String batchId,
                              String errorMessage, ImageMessage imageInfo) {
        try {
            ImageUploadMessage message = new ImageUploadMessage();
            message.setUserId(userId);
            message.setFileName(fileName);
            message.setFileSize(file.getSize());
            if (imageInfo != null) {
                message.setPicWidth(imageInfo.getPicWidth());
                message.setPicHeight(imageInfo.getPicHeight());
                message.setPicFormat(imageInfo.getPicFormat());
            }
            message.setBatchId(batchId);
            message.setErrorMessage(errorMessage);

//            imageProducer.sendErrorMessage(message);
        } catch (Exception e) {
            log.error("发送错误日志失败: fileName={}, error={}", fileName, e.getMessage());
        }
    }

    /**
     * 获取图片统计数据
     */
    @Override
    public R getStatistics() {
        try {
            log.info("开始获取图片统计数据");

            // 尝试从缓存获取
            Object cachedData = redisTemplate.opsForValue().get(PICTURE_STATISTICS_CACHE_KEY);
            if (cachedData != null) {
                log.info("从缓存获取统计数据成功");
                return ok(cachedData);
            }

            // 查询各状态的图片数量
            LambdaQueryWrapper<Picture> pendingWrapper = new LambdaQueryWrapper<>();
            pendingWrapper.eq(Picture::getIsDelete, 0)
                    .eq(Picture::getReviewStatus, PictureReviewStatusEnum.REVIEWING.getValue());
            long pendingCount = pictureMapper.selectCount(pendingWrapper);

            // 查询今日已审核数量
            LambdaQueryWrapper<Picture> todayReviewedWrapper = new LambdaQueryWrapper<>();
            todayReviewedWrapper.eq(Picture::getIsDelete, 0)
                    .in(Picture::getReviewStatus, PictureReviewStatusEnum.PASS.getValue(),
                            PictureReviewStatusEnum.REJECT.getValue())
                    .ge(Picture::getReviewTime, DateUtil.beginOfDay(new Date()));
            long todayReviewedCount = pictureMapper.selectCount(todayReviewedWrapper);

            // 查询违规图片数量
            LambdaQueryWrapper<Picture> violationsWrapper = new LambdaQueryWrapper<>();
            violationsWrapper.eq(Picture::getIsDelete, 0)
                    .eq(Picture::getReviewStatus, PictureReviewStatusEnum.REJECT.getValue());
            long violationsCount = pictureMapper.selectCount(violationsWrapper);

            // 查询总图片数量
            LambdaQueryWrapper<Picture> totalWrapper = new LambdaQueryWrapper<>();
            totalWrapper.eq(Picture::getIsDelete, 0);
            long totalCount = pictureMapper.selectCount(totalWrapper);

            // 构建返回数据
            ConcurrentHashMap<String, Object> statistics = new ConcurrentHashMap<>();
            statistics.put("pending", pendingCount);
            statistics.put("todayReviewed", todayReviewedCount);
            statistics.put("violations", violationsCount);
            statistics.put("total", totalCount);

            // 缓存统计数据
            redisTemplate.opsForValue().set(PICTURE_STATISTICS_CACHE_KEY, statistics,
                    STATISTICS_CACHE_EXPIRE_TIME, TimeUnit.MINUTES);
            log.info("统计数据已更新并缓存: pending={}, todayReviewed={}, violations={}, total={}",
                    pendingCount, todayReviewedCount, violationsCount, totalCount);

            return ok(statistics);
        } catch (Exception e) {
            log.error("获取图片统计数据失败", e);
            throw new ConditionException(e.getMessage());
        }
    }

    /**
     * 获取图片详情
     */
    @Override
    public R getPictureDetail(Long pictureId) {
        try {
            log.info("开始获取图片详情: pictureId={}", pictureId);

            Picture picture = pictureMapper.selectById(pictureId);
            if (picture == null) {
                log.warn("获取图片详情失败：未找到指定的图片 pictureId={}", pictureId);
                throw new ConditionException("未找到指定的图片");
            }

            // 转换为VO对象
            PictureVO pictureVO = PictureVO.objToVo(picture);

            log.info("获取图片详情成功: pictureId={}", pictureId);
            return ok(pictureVO);
        } catch (Exception e) {
            log.error("获取图片详情失败", e);
            throw new ConditionException(ResultMessageConstant.OPERATION_ERROR);
        }
    }

    /**
     * 更新图片信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updatePicture(Long pictureId, PictureUpdateDTO request) {
        try {
            log.info("开始更新图片信息: pictureId={}, request={}", pictureId, request);

            Picture picture = pictureMapper.selectById(pictureId);
            if (picture == null) {
                log.warn("更新图片信息失败：未找到指定的图片 pictureId={}", pictureId);
                throw new ConditionException("未找到指定的图片");
            }

            // 更新基本信息
            if (StrUtil.isNotBlank(request.getName())) {
                picture.setName(request.getName());
            }
            if (StrUtil.isNotBlank(request.getDescription())) {
                picture.setIntroduction(request.getDescription());
            }
            if (StrUtil.isNotBlank(request.getCategory())) {
                picture.setCategory(request.getCategory());
            }
            if (request.getTags() != null && !request.getTags().isEmpty()) {
                picture.setTagsList(request.getTags());
            }

            picture.setUpdateTime(new Date());
            pictureMapper.updateById(picture);

            // 删除相关缓存
            if (StrUtil.isNotBlank(request.getCategory())) {
                redisTemplate.delete(PICTURE_CATEGORIES_CACHE_KEY);
            }
            if (request.getTags() != null && !request.getTags().isEmpty()) {
                redisTemplate.delete(PICTURE_TAGS_CACHE_KEY);
            }

            log.info("更新图片信息成功: pictureId={}", pictureId);
            return ok("更新成功");
        } catch (Exception e) {
            log.error("更新图片信息失败", e);
            throw new ConditionException(ResultMessageConstant.OPERATION_ERROR);
        }
    }

    /**
     * 批量删除图片
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R batchDeletePictures(PictureBatchDeleteDTO request) {
        try {
            List<Long> pictureIds = request.getPictureIds();
            log.info("开始批量删除图片: pictureIds={}", pictureIds);
            ExHandler.throwIf(pictureIds == null || pictureIds.isEmpty(), "请选择要删除的图片");
            ExHandler.throwIf(pictureIds.isEmpty(), "未找到指定的图片");
            // 批量更新图片状态
            Date now = new Date();
            List<Long> failedIds = new ArrayList<>();
            if(request.getType()!= null){
                for (Long pictureId : pictureIds) {
                    try {
                        Aipicture picture = aipictureMapper.selectById(pictureId);
                        if (picture != null) {
                            aipictureMapper.updateDelStatusById(picture.getId(), PictureDeleteStatusEnum.IS_DELETE.getValue(), now);
                            if (StrUtil.isNotBlank(picture.getUrl())) {
                                cosManager.deleteObject(picture.getUrl());
                            }
                            if (StrUtil.isNotBlank(picture.getThumbnailUrl())) {
                                cosManager.deleteObject(picture.getThumbnailUrl());
                            }
                        }
                    } catch (Exception ex) {
                        log.error("删除AI图片或COS对象失败，pictureId={}, error={}", pictureId, ex.getMessage(), ex);
                        failedIds.add(pictureId);
                    }
                }
                if (!failedIds.isEmpty()) {
                    return no("部分AI图片删除失败，失败ID：" + failedIds);
                }
            } else {
                for (Long pictureId : pictureIds) {
                    try {
                        Picture picture = pictureMapper.selectById(pictureId);
                        if (picture != null) {
                            pictureMapper.updateDelStatusById(picture.getId(), PictureDeleteStatusEnum.IS_DELETE.getValue(), now);
                            if (StrUtil.isNotBlank(picture.getUrl())) {
                                cosManager.deleteObject(picture.getUrl());
                            }
                            if (StrUtil.isNotBlank(picture.getThumbnailUrl())) {
                                cosManager.deleteObject(picture.getThumbnailUrl());
                            }
                        }
                    } catch (Exception ex) {
                        log.error("删除AI图片或COS对象失败，pictureId={}, error={}", pictureId, ex.getMessage(), ex);
                        failedIds.add(pictureId);
                    }
                }
                // 删除统计数据缓存
                redisTemplate.delete(CACHE_DATA_KEY + generateCacheKey(1, 35));
                redisTemplate.delete(PICTURE_STATISTICS_CACHE_KEY);
            }

            log.info("批量删除完成: count={}", pictureIds.size());
            return ok("删除成功");
        } catch (Exception e) {
            log.error("批量删除图片失败", e);
            throw new ConditionException(ResultMessageConstant.OPERATION_ERROR);
        }
    }

    /**
     * 标准化颜色格式
     * 统一转换为6位16进制颜色码，例如：#050200
     *
     * @param color 输入的颜色值，可能是以下格式：
     *              - 前端传入：050200 或 #050200
     *              - 腾讯云返回：#0x3e2819 或 0x3e2819
     * @return 标准化后的颜色值，格式为：#050200
     */
    private String standardizeColorFormat(String color) {
        if (StrUtil.isBlank(color)) {
            log.debug("颜色值为空");
            return null;
        }
        log.debug("开始标准化颜色格式: rawColor={}", color);

        // 1. 移除所有非十六进制字符（保留0-9和a-fA-F）
        String processedColor = color.replaceAll("[^0-9A-Fa-f]", "");
        log.debug("移除非十六进制字符后: processedColor={}", processedColor);

        // 2. 处理腾讯云返回的0x前缀格式
        if (color.contains("0x")) {
            processedColor = processedColor.substring(processedColor.indexOf("0x") + 2);
            log.debug("处理0x前缀后: processedColor={}", processedColor);
        }

        // 3. 确保是6位
        if (processedColor.length() >= 6) {
            // 取后6位，因为有些颜色值可能超过6位
            processedColor = processedColor.substring(processedColor.length() - 6);
            String standardColor = "0x" + processedColor.toLowerCase();
            log.debug("颜色标准化完成: rawColor={} -> standardColor={}", color, standardColor);
            return standardColor;
        } else {
            log.warn("颜色格式无效（长度不足6位）: processedColor={}", processedColor);
            return null;
        }
    }

    /**
     * 根据颜色搜索图片
     */
    @Override
    public R searchByColor(String color) {
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        ExHandler.throwIf(userSessionVO == null, ResultMessageConstant.PLEASE_LOGIN);
        Long userId = userSessionVO.getId();
        try {
            log.info("接收到颜色搜索请求: color={}", color);

            // 标准化搜索颜色格式
            String standardColor = standardizeColorFormat(color);
            if (standardColor == null) {
                log.warn("颜色格式无效，无法执行搜索: rawColor={}", color);
                throw new ConditionException("无效的颜色格式");
            }
            log.info("颜色标准化成功: rawColor={} -> standardColor={}", color, standardColor);

            // 查询所有有颜色信息的图片
            LambdaQueryWrapper<Picture> wrapper = new LambdaQueryWrapper<>();
            wrapper.isNotNull(Picture::getPicColor)
                    .eq(Picture::getIsDelete, PictureDeleteStatusEnum.NOT_DELETE.getValue())
                    .eq(Picture::getUserId, userId)
                    .eq(Picture::getReviewStatus, PictureReviewStatusEnum.PASS.getValue());

            List<Picture> pictureList = pictureMapper.selectList(wrapper);
            log.info("查询到的图片数量: total={}", pictureList.size());

            if (CollUtil.isEmpty(pictureList)) {
                log.info("未找到任何带有颜色信息的图片");
                return ok(Collections.emptyList());
            }

            // 将搜索颜色转换为Color对象
            final Color targetColor = Color.decode(standardColor);
            log.debug("目标颜色解析成功: standardColor={}, RGB=({},{},{})",
                    standardColor, targetColor.getRed(), targetColor.getGreen(), targetColor.getBlue());

            // 计算相似度并排序
            log.info("开始计算颜色相似度并排序: 总图片数={}", pictureList.size());
            List<Picture> sortedPictureList = pictureList.stream()
                    .filter(picture -> StrUtil.isNotBlank(picture.getPicColor()))
                    .map(picture -> {
                        try {
                            // 标准化数据库中的颜色
                            String dbColor = standardizeColorFormat(picture.getPicColor());
                            if (dbColor != null) {
                                picture.setPicColor(dbColor);
                                log.debug("图片颜色标准化成功: pictureId={}, rawColor={} -> standardColor={}",
                                        picture.getId(), picture.getPicColor(), dbColor);
                                return picture;
                            }
                            log.warn("图片颜色标准化失败: pictureId={}, rawColor={}",
                                    picture.getId(), picture.getPicColor());
                            return null;
                        } catch (Exception e) {
                            log.warn("图片颜色处理异常: pictureId={}, color={}, error={}",
                                    picture.getId(), picture.getPicColor(), e.getMessage());
                            return null;
                        }
                    })
                    .filter(Objects::nonNull)
                    .sorted((p1, p2) -> {
                        try {
                            Color c1 = Color.decode(p1.getPicColor());
                            Color c2 = Color.decode(p2.getPicColor());
                            double similarity1 = ImageUtil.calculateSimilarity(targetColor, c1);
                            double similarity2 = ImageUtil.calculateSimilarity(targetColor, c2);
                            log.debug(
                                    "颜色相似度比较: pic1=[id={}, color={}, similarity={}], pic2=[id={}, color={}, similarity={}]",
                                    p1.getId(), p1.getPicColor(), similarity1,
                                    p2.getId(), p2.getPicColor(), similarity2);
                            return Double.compare(similarity2, similarity1); // 降序排序
                        } catch (Exception e) {
                            log.warn("颜色相似度计算异常: pic1=[id={}, color={}], pic2=[id={}, color={}], error={}",
                                    p1.getId(), p1.getPicColor(),
                                    p2.getId(), p2.getPicColor(),
                                    e.getMessage());
                            return 0;
                        }
                    })
                    .limit(35)
                    .collect(Collectors.toList());

            log.info("颜色相似度排序完成: 符合条件的图片数={}", sortedPictureList.size());

            // 返回结果
            List<PictureVO> voList = sortedPictureList.stream()
                    .map(PictureVO::objToVo)
                    .collect(Collectors.toList());

            log.info("颜色搜索完成: 返回结果数={}", voList.size());
            return ok(voList);
        } catch (Exception e) {
            log.error("颜色搜索发生异常: color={}, error={}", color, e.getMessage(), e);
            throw new ConditionException("搜索失败");
        }
    }

    /**
     * 编辑图片
     * 支持：
     * 1. 仅更新图片信息（分类、标签、描述）
     * 2. 更新图片文件和信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R editPicture(Long pictureId, MultipartFile file, String tags,
                              String category, String description) {
        try {
            log.info("开始处理图片编辑请求: pictureId={}, hasNewFile={}, newTags={}, newCategory={}",
                    pictureId, file != null && !file.isEmpty(), tags, category);

            // 1. 获取用户信息
            UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
            if (userSessionVO == null) {
                log.warn("图片编辑失败：用户未登录");
                throw new ConditionException(ResultMessageConstant.NOT_LOGIN);
            }

            // 2. 获取原图片信息
            Picture oldPicture = pictureMapper.selectById(pictureId);
            if (oldPicture == null) {
                log.warn("图片编辑失败：未找到原图片信息 pictureId={}", pictureId);
                throw new ConditionException("未找到原图片");
            }

            // 3. 权限检查
            if (!oldPicture.getUserId().equals(userSessionVO.getId()) &&
                    !UserRoleEnum.ADMIN.getDesc().equals(userSessionVO.getUserRole())) {
                log.warn("图片编辑失败：无权限 pictureId={}, userId={}", pictureId, userSessionVO.getId());
                throw new ConditionException("无权编辑该图片");
            }

            // 4. 更新图片信息
            boolean isInfoUpdated = false;
            if (StrUtil.isNotBlank(tags)) {
                // 将逗号分隔的标签字符串转换为List
                List<String> tagList = StrUtil.split(tags, ',').stream()
                        .map(String::trim)
                        .filter(StrUtil::isNotBlank)
                        .collect(Collectors.toList());
                oldPicture.setTagsList(tagList);
                isInfoUpdated = true;
            }
            if (StrUtil.isNotBlank(category)) {
                oldPicture.setCategory(category);
                isInfoUpdated = true;
            }
            if (StrUtil.isNotBlank(description)) {
                oldPicture.setIntroduction(description);
                isInfoUpdated = true;
            }

            // 5. 处理图片文件更新（如果有）
            boolean isFileUpdated = false;
            if (file != null && !file.isEmpty()) {
                log.info("检测到新图片文件，开始处理图片更新: pictureId={}, fileName={}",
                        pictureId, file.getOriginalFilename());

                // 5.1 验证新图片
                if (file.getSize() > 2 * 1024 * 1024) {
                    log.warn("图片大小超过限制: fileName={}, size={}KB",
                            file.getOriginalFilename(), file.getSize() / 1024);
                    throw new ConditionException(ResultMessageConstant.FILE_SIZE_ERROR);
                }

                // 5.2 处理新文件上传
                // 使用时间戳生成文件名
                String originalFilename = file.getOriginalFilename();
                String extension = "";
                int dotIndex = originalFilename.lastIndexOf('.');
                if (dotIndex > 0) {
                    extension = originalFilename.substring(dotIndex + 1);
                }
                if (extension.isEmpty()) {
                    extension = "jpg";
                }
                String fileName = System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8) + "." + extension;
                try {
                    // 1. 校验图片
                    ImageUtil.validate(file);

                    // 2. 获取图片信息
                    ImageMessage imageInfo = ImageUtil.getImageMessage(file);

                    // 3. 创建临时文件
                    File tempFile = File.createTempFile("upload_", "_" + fileName);
                    try {
                        file.transferTo(tempFile);

                        // 4. 上传到COS
                        boolean isAdmin = UserRoleEnum.ADMIN.getDesc().equals(userSessionVO.getUserRole());
                        String basePath = isAdmin ? "/public/" : "/user/";
                        String cosPath = basePath + fileName;
                        Map<String, String> uploadResult = cosManager.putObjectAndGetColor(cosPath, tempFile);

                        // 获取图片颜色信息
                        String mainColor = uploadResult.get("mainColor");
                        String standardColor = mainColor != null ? "#" + mainColor : null;

                        // 5. 删除原COS文件（只有在新文件上传成功后才删除旧文件）
                        try {
                            cosManager.deleteObject(oldPicture.getUrl());
                            // 如果有缩略图也需要删除
                            if (StrUtil.isNotBlank(oldPicture.getThumbnailUrl())) {
                                String thumbnailPath = oldPicture.getThumbnailUrl().split("\\?")[0];
                                cosManager.deleteObject(thumbnailPath);
                            }
                            log.info("原COS文件删除成功: pictureId={}, url={}", pictureId, oldPicture.getUrl());
                        } catch (Exception e) {
                            log.error("删除原COS文件失败: pictureId={}, url={}, error={}",
                                    pictureId, oldPicture.getUrl(), e.getMessage());
                            // 继续执行，因为新文件已经上传成功
                        }

                        // 6. 更新图片信息
                        String webpPath = cosPath + "?imageMogr2/format/webp/quality/60";
                        oldPicture.setUrl(cosPath);
                        oldPicture.setThumbnailUrl(webpPath);
                        oldPicture.setFileSize(file.getSize());
                        oldPicture.setPicWidth(imageInfo.getPicWidth());
                        oldPicture.setPicHeight(imageInfo.getPicHeight());
                        oldPicture.setPicScale(imageInfo.getPicScale());
                        oldPicture.setPicFormat(imageInfo.getPicFormat());
                        oldPicture.setPicColor(standardColor);
                        oldPicture.setPicSize(imageInfo.getPicSize());
                        isFileUpdated = true;

                        log.info("新图片处理成功: pictureId={}, newUrl={}", pictureId, cosPath);
                    } finally {
                        // 清理临时文件
                        if (tempFile != null && tempFile.exists()) {
                            tempFile.delete();
                        }
                    }
                } catch (Exception e) {
                    log.error("新图片处理失败: pictureId={}, error={}", pictureId, e.getMessage());
                    throw new ConditionException("图片处理失败");
                }
            }

            // 6. 如果有任何更新，更新数据库
            if (isInfoUpdated || isFileUpdated) {
                oldPicture.setUpdateTime(new Timestamp(System.currentTimeMillis()));
                oldPicture.setIsEdit(1); // 标记为已编辑
                pictureMapper.updateById(oldPicture);
                log.info("图片编辑完成: pictureId={}, infoUpdated={}, fileUpdated={}",
                        pictureId, isInfoUpdated, isFileUpdated);
                return ok("编辑成功");
            } else {
                log.info("图片无任何更新: pictureId={}", pictureId);
                return ok("无更新内容");
            }
        } catch (Exception e) {
            log.error("图片编辑失败: pictureId={}, error={}", pictureId, e.getMessage());
            throw new ConditionException(ResultMessageConstant.OPERATION_ERROR);
        }
    }

    /**
     * 获取上传信息列表
     *
     * @param page
     * @param pageSize
     * @param description
     * @param reviewStatus
     * @param category
     * @param tags
     * @return
     */
    @Override
    public R getUploadingPictures(Integer page, Integer pageSize, String description, Integer reviewStatus, String category, String tags) {
        try {
            log.info("开始获取待审核图片列表: page={}, pageSize={}, description={}, category={}, tags={}",
                    page, pageSize, description, category, tags);

            page = page == null ? 1 : page;
            pageSize = pageSize == null ? DEFAULT_PAGE_SIZE : pageSize;

            LambdaQueryWrapper<Picture> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Picture::getReviewStatus, PictureReviewStatusEnum.REVIEWING.getValue())
                    .eq(Picture::getIsDelete, PictureDeleteStatusEnum.NOT_DELETE.getValue())
                    .eq(reviewStatus != null, Picture::getReviewStatus, reviewStatus)
                    .like(StrUtil.isNotBlank(description), Picture::getIntroduction, description)
                    .like(StrUtil.isNotBlank(category), Picture::getCategory, category);

            // 处理标签查询
            if (StrUtil.isNotBlank(tags)) {
                log.debug("处理标签查询条件: tags={}", tags);
                String[] tagArray = tags.split(",");
                wrapper.and(w -> {
                    for (int i = 0; i < tagArray.length; i++) {
                        String tag = tagArray[i].trim();
                        if (StrUtil.isNotBlank(tag)) {
                            if (i == 0) {
                                w.like(Picture::getTags, tag);
                            } else {
                                w.or().like(Picture::getTags, tag);
                            }
                        }
                    }
                });
            }

            wrapper.orderByDesc(Picture::getCreateTime);

            Page<Picture> picturePage = new Page<>(page, pageSize);
            Page<Picture> resultPage = pictureMapper.selectPage(picturePage, wrapper);
            log.info("查询待审核图片完成: 总数={}, 当前页数据量={}",
                    resultPage.getTotal(), resultPage.getRecords().size());

            // 构建返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("total", resultPage.getTotal());
            result.put("list", resultPage.getRecords().stream()
                    .map(PictureVO::objToVo)
                    .collect(Collectors.toList()));

            return ok(result);
        } catch (Exception e) {
            log.error("获取待审核图片列表失败", e);
            throw new ConditionException(ResultMessageConstant.OPERATION_ERROR);
        }
    }

    /**
     * 批量获取必应图片链接
     *
     * @param pictureUploadByBatchRequest 请求参数
     * @param userSessionVO               用户信息
     * @return 图片链接列表
     */
    @Override
    public R getBingImageUrls(PictureUploadByBatchDTO pictureUploadByBatchRequest, UserSessionVO userSessionVO) {
        try {
            // 校验参数
            String searchText = pictureUploadByBatchRequest.getSearchText();
            Integer count = pictureUploadByBatchRequest.getCount();
            ExHandler.throwIf(count > 1000, "最多获取30条图片链接");

            log.info("开始获取必应图片链接: searchText={}, count={}", searchText, count);

            // 生成缓存键
            String cacheKey = String.format(BING_SEARCH_CACHE_KEY, searchText);

            // 获取已缓存的URL集合
            Set<String> usedUrls = new HashSet<>();
            Object cachedUrls = redisTemplate.opsForValue().get(cacheKey);
            if (cachedUrls != null) {
                usedUrls = new HashSet<>((List<String>) cachedUrls);
                log.info("找到缓存的URL列表: searchText={}, 已使用数量={}", searchText, usedUrls.size());
            }

            // 存储新的图片信息
            List<Map<String, String>> imageInfoList = new ArrayList<>();
            List<String> newUrls = new ArrayList<>();
            int offset = 0;
            int maxAttempts = 5; // 最大尝试次数，防止无限循环
            int attempts = 0;

            while (imageInfoList.size() < count && attempts < maxAttempts) {
                attempts++;
                log.info("第{}次尝试获取图片, 当前已收集{}张, 目标{}张", attempts, imageInfoList.size(), count);

                // 构建带偏移量的URL
                String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s&first=%d&count=100&mmasync=1",
                        searchText, offset);
                Document document;
                try {
                    document = Jsoup.connect(fetchUrl).get();
                } catch (IOException e) {
                    log.error("获取必应图片页面失败: error={}", e.getMessage());
                    break;
                }

                // 解析内容
                Element div = document.getElementsByClass("dgControl").first();
                if (ObjUtil.isEmpty(div)) {
                    log.warn("未找到更多图片元素");
                    break;
                }

                // 获取所有图片元素
                Elements imgElementList = div.select("img.mimg");
                if (imgElementList.isEmpty()) {
                    log.warn("未找到更多符合条件的图片");
                    break;
                }

                boolean foundNewImages = false;
                // 遍历元素，获取图片链接
                for (Element imgElement : imgElementList) {
                    String fileUrl = imgElement.attr("src");
                    if (StrUtil.isBlank(fileUrl)) {
                        continue;
                    }

                    // 处理图片URL，移除参数部分
                    int questionMarkIndex = fileUrl.indexOf("?");
                    if (questionMarkIndex > -1) {
                        fileUrl = fileUrl.substring(0, questionMarkIndex);
                    }

                    // 如果URL已经被使用过，跳过
                    if (usedUrls.contains(fileUrl)) {
                        log.debug("跳过已使用的URL: {}", fileUrl);
                        continue;
                    }

                    // 获取图片标题（如果有的话）
                    String title = imgElement.attr("alt");
                    if (StrUtil.isBlank(title)) {
                        title = searchText + "_" + (imageInfoList.size() + 1);
                    }

                    // 构建图片信息
                    Map<String, String> imageInfo = new HashMap<>();
                    imageInfo.put("url", fileUrl);
                    imageInfo.put("title", title);

                    imageInfoList.add(imageInfo);
                    newUrls.add(fileUrl);
                    foundNewImages = true;

                    if (imageInfoList.size() >= count) {
                        break;
                    }
                }

                // 如果这次没有找到新图片，增加偏移量继续查找
                if (!foundNewImages) {
                    log.info("当前页面未找到新图片，尝试下一页");
                }
                offset += 100; // 增加偏移量
            }

            // 如果没有找到足够的新图片
            if (imageInfoList.isEmpty()) {
                log.warn("未找到新的图片链接: searchText={}", searchText);
                throw new ConditionException("没有更多图片了");
            }

            // 更新缓存中的URL列表
            usedUrls.addAll(newUrls);
            redisTemplate.opsForValue().set(cacheKey, new ArrayList<>(usedUrls), BING_CACHE_EXPIRE_TIME, TimeUnit.HOURS);

            log.info("获取必应图片链接完成: searchText={}, 新增数量={}, 总缓存数量={}, 尝试次数={}",
                    searchText, imageInfoList.size(), usedUrls.size(), attempts);

            // 构建返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("total", imageInfoList.size());
            result.put("list", imageInfoList);

            return ok(result);

        } catch (Exception e) {
            log.error("获取必应图片链接失败: error={}", e.getMessage());
            throw new ConditionException(e.getMessage());
        }
    }

    /**
     * 批量修改图片收藏状态
     *
     * @param picIds 逗号分隔的图片ID字符串
     * @return
     */
    @Override
    public R batchLikeFavoriteStatus(String picIds) {
        UserSessionVO userSessionVO = (UserSessionVO) httpSession.getAttribute(KeysConstant.LOGIN_SESSION_KEY);
        Long userId = userSessionVO.getId();
        ExHandler.throwIf(userId == null || StringUtils.isBlank(picIds), "参数错误");
        String[] picIdArr = picIds.split(",");
        Set<Long> picIdSet = Arrays.stream(picIdArr)
                .filter(StringUtils::isNotBlank)
                .map(Long::parseLong)
                .collect(Collectors.toSet());
        // 点赞
        List<LikePicture> likeList = likePictureMapper.selectList(
                new LambdaQueryWrapper<LikePicture>()
                        .eq(LikePicture::getLikeUserId, userId)
                        .in(LikePicture::getPicId, picIdSet)
        );
        System.out.println("点赞列表:" + likeList.toString());
        Map<Long, LikePicture> likeMap = likeList.stream().collect(Collectors.toMap(LikePicture::getPicId, l -> l));
        // 收藏
        List<FavoritePicture> favoriteList = favoritePictureMapper.selectList(
                new LambdaQueryWrapper<FavoritePicture>()
                        .eq(FavoritePicture::getFavoriteUserId, userId)
                        .in(FavoritePicture::getPicId, picIdSet)
        );
        System.out.println("收藏列表:" + favoriteList.toString());
        Map<Long, FavoritePicture> favoriteMap = favoriteList.stream().collect(Collectors.toMap(FavoritePicture::getPicId, f -> f));
        // 组装结果
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (Long picId : picIdSet) {
            boolean isLiked = false;
            LikePicture like = likeMap.get(picId);
            if (like != null && like.getIsDelete() != null && like.getIsDelete() == PictureDeleteStatusEnum.NOT_DELETE.getValue()) {
                isLiked = true;
            }
            boolean isFavorited = false;
            FavoritePicture fav = favoriteMap.get(picId);
            if (fav != null && fav.getIsDelete() != null && fav.getIsDelete() == PictureDeleteStatusEnum.NOT_DELETE.getValue()) {
                isFavorited = true;
            }
            Map<String, Object> map = new HashMap<>();
            map.put("picId", picId.toString());
            map.put("isLiked", isLiked);
            map.put("isFavorited", isFavorited);
            resultList.add(map);
        }
        return ok(resultList);
    }
}